package com.iocup.keybastion.configuration;


import com.iocup.keybastion.authentication.TokenBuilder;
import com.iocup.keybastion.authentication.TokenService;
import com.iocup.keybastion.authentication.authenticator.AbstractAuthenticator;
import com.iocup.keybastion.authentication.authenticator.UsernamePasswordAuthenticator;
import com.iocup.keybastion.authentication.impl.DefaultTokenBuilder;
import com.iocup.keybastion.authentication.impl.DefaultTokenService;
import com.iocup.keybastion.authentication.userdetails.password.BCryptPasswordEncoder;
import com.iocup.keybastion.authentication.userdetails.password.DefaultPasswordVerifiers;
import com.iocup.keybastion.authentication.userdetails.password.PasswordEncoder;
import com.iocup.keybastion.authentication.userdetails.password.PasswordVerifiers;
import com.iocup.keybastion.authentication.userdetails.service.MockUserDetailServiceImpl;
import com.iocup.keybastion.authentication.userdetails.service.UserDetailService;
import com.iocup.keybastion.authorize.decision.*;
import com.iocup.keybastion.authorize.element.AuthElementProvider;
import com.iocup.keybastion.authorize.element.AuthElementService;
import com.iocup.keybastion.authorize.element.DefaultAuthElementService;
import com.iocup.keybastion.common.AuthConstant;
import com.iocup.keybastion.core.SecurityPostLogicChecker;
import com.iocup.keybastion.core.SecurityPreLogicChecker;
import com.iocup.keybastion.core.client.BaseClient;
import com.iocup.keybastion.core.client.Client;
import com.iocup.keybastion.core.client.ClientFinder;
import com.iocup.keybastion.core.client.DefaultSecurityClientFinder;
import com.iocup.keybastion.core.lock.LocalLock;
import com.iocup.keybastion.core.lock.Lock;
import com.iocup.keybastion.core.matching.Matcher;
import com.iocup.keybastion.core.matching.MatchingChecker;
import com.iocup.keybastion.core.matching.impl.DefaultMatchingChecker;
import com.iocup.keybastion.core.session.DefaultSessionIdGenerator;
import com.iocup.keybastion.core.session.SessionIdGenerator;
import com.iocup.keybastion.core.store.CacheStore;
import com.iocup.keybastion.core.store.InMemoryCacheStore;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 * 认证配置
 *
 * @author xyjxust
 * @create 2022/2/23 14:25
 **/
@Getter
@Slf4j
public class AuthenticationConfigurator {


    /**
     * basic认证决断器
     */
    private BasicAuthDecision basicAuthDecision = new BasicAuthDecision(new BasicProperties());

    /**
     * 数据源提供者
     */
    private AuthElementService authElementService = new DefaultAuthElementService();
    /**
     * 权限决断查找器
     */
    private AuthDecisionFinder finder = new DefaultAuthDecisionFinder();
    /**
     * 登录客户端查找器
     */
    private ClientFinder clientFinder = new DefaultSecurityClientFinder();

    /**
     * session存储器
     */
    private CacheStore cacheStore = new InMemoryCacheStore();

    /**
     * token构建器
     */
    private TokenBuilder tokenBuilder;

    /**
     * token服务类
     */
    private TokenService tokenService;

    /**
     * 认证器
     */
    private final Map<String, AbstractAuthenticator> authenticators = new HashMap<>();
    /**
     * 认证客户端
     */
    private final List<Client> clients = new ArrayList<>();

    /**
     * sessionId生成器
     */
    private SessionIdGenerator sessionIdGenerator = new DefaultSessionIdGenerator();
    /**
     * token的相关配置
     */
    private TokenProperties tokenProperties = new TokenProperties();
    /**
     * 用户信息查询服务类
     */
    private UserDetailService userDetailService;
    /**
     * 密码编码器
     */
    private PasswordEncoder passwordEncoder;
    /**
     * 密码判断器
     */
    private PasswordVerifiers passwordVerifiers;
    /**
     * 锁服务类：默认为本地锁
     */
    private Lock lock;
    /**
     * token提取配置类
     */
    private TokenExtractProperties tokenExtractProperties = new TokenExtractProperties();
    /**
     * 一些总体配置
     */
    private SecurityProperties securityProperties = new SecurityProperties();

    /**
     * 匹配检查器
     */
    private MatchingChecker matchingChecker = new DefaultMatchingChecker();

    /**
     * 登录之前的一些逻辑
     */
    private List<SecurityPreLogicChecker>  preCheckers = new ArrayList<>();

    /**
     * 登录之后的逻辑
     */
    private List<SecurityPostLogicChecker>  postCheckers = new ArrayList<>();


    /**
     * 添加匹配器：按照添加顺序进行匹配
     *
     * @param matchers
     * @return
     */
    public AuthenticationConfigurator addMatcher(Matcher... matchers) {
        matchingChecker.addMatcher(matchers);
        return this;
    }


    public AuthenticationConfigurator basicAuthConfigurator(BasicProperties basicProperties) {
        basicAuthDecision = new BasicAuthDecision(basicProperties);
        return this;
    }


    /**
     * 添加权限元素来源
     *
     * @param authElementProvider
     * @return
     */
    public AuthenticationConfigurator addAuthElementProvider(AuthElementProvider authElementProvider) {
        this.authElementService.addProvider(authElementProvider);
        return this;
    }

    /**
     * 添加决断器
     *
     * @param decision
     * @return
     */
    public AuthenticationConfigurator addDecision(AuthDecision decision) {
        this.finder.addDecision(decision);
        return this;
    }

    /**
     * 設置当前认证的客户端
     *
     * @param client
     * @return
     */
    public AuthenticationConfigurator addClient(BaseClient client) {
        client.setCacheStore(getCacheStore());
        this.clientFinder.addClient(client);
        return this;
    }

    /**
     * 添加登录的客户端信息
     *
     * @param authenticator
     * @return
     */
    public AuthenticationConfigurator addAuthenticator(AbstractAuthenticator authenticator) {
        authenticators.put(authenticator.getType(), authenticator);
        return this;
    }

    /**
     * 设置token的构建器
     *
     * @param tokenBuilder
     * @return
     */
    public AuthenticationConfigurator tokenBuilder(TokenBuilder tokenBuilder) {
        this.tokenBuilder = tokenBuilder;
        return this;
    }

    /**
     * 设置token的构建器
     *
     * @param tokenProperties
     * @return
     */
    public AuthenticationConfigurator tokenProperties(TokenProperties tokenProperties) {
        this.tokenProperties = tokenProperties;
        return this;
    }

    public AuthenticationConfigurator userDetailService(UserDetailService userDetailService) {
        this.userDetailService = userDetailService;
        return this;
    }

    public AuthenticationConfigurator lock(Lock lock) {
        this.lock = lock;
        return this;
    }

    public AuthenticationConfigurator tokenExtractProperties(TokenExtractProperties tokenExtractProperties) {
        this.tokenExtractProperties = tokenExtractProperties;
        return this;
    }

    public AuthenticationConfigurator sessionStore(CacheStore cacheStore) {
        this.cacheStore = cacheStore;
        return this;
    }

    public AuthenticationConfigurator securityProperties(SecurityProperties securityProperties) {
        this.securityProperties = securityProperties;
        return this;
    }

    /**
     * 添加前置检查器
     * @param securityLogicChecker
     * @return
     */
    public AuthenticationConfigurator addPreChecker(SecurityPreLogicChecker securityLogicChecker) {
        preCheckers.add(securityLogicChecker);
        return this;
    }

    /**
     * 添加后置检查器
     * @param securityLogicChecker
     * @return
     */
    public AuthenticationConfigurator addPostChecker(SecurityPostLogicChecker securityLogicChecker) {
        postCheckers.add(securityLogicChecker);
        return this;
    }


    public CacheStore getCacheStore() {
        return cacheStore == null ? new InMemoryCacheStore() : this.cacheStore;
    }

    public TokenBuilder getTokenBuilder() {
        return tokenBuilder == null ? new DefaultTokenBuilder(getTokenProperties()) : tokenBuilder;
    }

    public TokenService getTokenService() {
        return tokenService == null ? new DefaultTokenService(getLock(), getTokenProperties(), getTokenBuilder(), getSessionIdGenerator(), getCacheStore()) : tokenService;
    }

    public ClientFinder getClientFinder() {
        return clientFinder;
    }

    public TokenProperties getTokenProperties() {
        return tokenProperties == null ? new TokenProperties() : this.tokenProperties;
    }

    public PasswordEncoder getPasswordEncoder() {
        return passwordEncoder == null ? new BCryptPasswordEncoder() : passwordEncoder;
    }

    public PasswordVerifiers getPasswordVerifiers() {
        return new DefaultPasswordVerifiers(getPasswordEncoder());
    }

    public UserDetailService getUserDetailService() {
        if (userDetailService == null) {
            return new MockUserDetailServiceImpl(null);
        }
        return this.userDetailService;
    }

    public Lock getLock() {
        return lock == null ? new LocalLock() : this.lock;
    }


    public TokenExtractProperties getTokenExtractProperties() {
        return tokenExtractProperties == null ? new TokenExtractProperties() : this.tokenExtractProperties;
    }

    public Collection<AbstractAuthenticator> getAuthenticators() {
        if (!authenticators.containsKey(AuthConstant.DEFAULT_AUTH_TYPE)) {
            authenticators.put(AuthConstant.DEFAULT_AUTH_TYPE, new UsernamePasswordAuthenticator());
        }

        authenticators.values().forEach(abstractAuthenticator -> {
            abstractAuthenticator.setPasswordVerifiers(getPasswordVerifiers());
            abstractAuthenticator.setTokenService(getTokenService());
            abstractAuthenticator.setUserDetailService(getUserDetailService());
        });
        return authenticators.values();
    }
}
